/*
 * Selector.cpp
 *
 *  Created on: Jul 14, 2016
 *      Author: Lucifer
 */

#include "Selector.h"

namespace BFX {

void Selector::processCanceledKeys() {
	boost::lock_guard<boost::recursive_mutex> sync(_keyLock);
	for (std::set<REF<SelectionKey> >::iterator i = _canceledKeys.begin();
			i != _canceledKeys.end(); i++) {
		SelectionKey* key = (*i);
		unregisterImpl(key);
	}
	_canceledKeys.clear();
}

/// @copydoc Selector::registerKey()
void DefaultSelector::registerImpl(SelectionKey* key) {
	boost::lock_guard<boost::recursive_mutex> sync(_closeLock);
	fildes_t fd = key->getChannel()->getDescriptor();
	_keyMap.insert(std::pair<fildes_t, REF<SelectionKey> >(fd, key));
}
/// @copydoc Selector::unregisterImpl()
void DefaultSelector::unregisterImpl(SelectionKey* key) {
	boost::lock_guard<boost::recursive_mutex> sync(_closeLock);
	fildes_t fd = key->getChannel()->getDescriptor();
	_keyMap.erase(fd);
}

void DefaultSelector::closeImpl() {
	boost::lock_guard<boost::recursive_mutex> sync(_closeLock);
	for (KeyMap::iterator i = _keyMap.begin(); i != _keyMap.end(); i++) {
		SelectionKey* key = i->second;
		unregisterKey(key);
		// XXX if there is no more registered key(s) for this channel, kill it.
		// e.g.,
		// SelectableChannel* ch = key->getChannel();
		// if (ch->isOpen() && ch->isRegistered())	ch->kill();
	}
	_keyMap.clear();
}

int DefaultSelector::selectImpl(long timeout) {
	// process unregistered queue
	processCanceledKeys();

	fildes_t maxFd = prepareFDSets();

	// Setting up timeout.
	struct timeval tv, *ptv;
	static struct timeval ztv = { 0, 0 };
	if (timeout == 0) {
		ptv = &ztv;
	} else if (timeout < 0) {
		ptv = NULL;
	} else {
		ptv = &tv;
		ptv->tv_sec = (long) (timeout / 1000);
		ptv->tv_usec = (long) ((timeout % 1000) * 1000);
	}

	// Call select
	int retval = ::select(maxFd + 1, _fdSets[FS_READ], _fdSets[FS_WRITE], _fdSets[FS_EXCEPT],
			ptv);
	if (retval == -1) {
		// TODO
		return -1;
	}

	// Reset the interrupter.
	if (retval > 0 && _fdSets[FS_READ].isSet(_interrupter.getReadDescriptor())) {
		_interrupter.reset();
		retval--;
	}

	processCanceledKeys();
	if (retval > 0) {
		processSelectedKeys();
	}

	return retval;
}

int DefaultSelector::prepareFDSets() {
	// Reset all descriptor sets.
	for (int i = 0; i < FS_MAX; i ++) {
		_fdSets[i].reset();
	}
	// Set interrupter here
	_fdSets[FS_READ].set(_interrupter.getReadDescriptor());

	// Set FD_SET structures required for select
	for (KeyMap::iterator i = _keyMap.begin(); i != _keyMap.end(); i++) {
		fildes_t fd = i->first;
		SelectionKey* key = i->second;
		if (key->getInterestOps() & (SelectionKey::OP_READ | SelectionKey::OP_ACCEPT)) {
			_fdSets[FS_READ].set(fd);
		}
		if (key->getInterestOps() & (SelectionKey::OP_WRITE | SelectionKey::OP_CONNECT)) {
			// NOTE: Connection on Windows use both except and write fd_sets.
			_fdSets[FS_WRITE].set(fd);
		}
		_fdSets[FS_EXCEPT].set(fd);
	}
	return _fdSets[FS_EXCEPT].getMaxDescriptor();
}

int DefaultSelector::processSelectedKeys() {
	int numUpdated = 0;

	for (KeyMap::iterator i = _keyMap.begin(); i != _keyMap.end(); i++) {
		fildes_t fd = i->first;
		SelectionKey* key = i->second;
		int readyOps = 0;

		if (_fdSets[FS_READ].isSet(fd)) {
			readyOps |= (SelectionKey::OP_READ | SelectionKey::OP_ACCEPT);
		}
		if (_fdSets[FS_WRITE].isSet(fd)) {
			// NOTE: Connection on Windows use both except and write fd_sets.
			readyOps |= (SelectionKey::OP_WRITE | SelectionKey::OP_CONNECT);
		}
		if (_fdSets[FS_EXCEPT].isSet(fd)) {
			readyOps |= (SelectionKey::OP_READ | SelectionKey::OP_WRITE
					| SelectionKey::OP_CONNECT | SelectionKey::OP_ACCEPT);
		}

		if (readyOps != 0) {
			// update ready ops for the current key.
			key->getChannel()->setReadyOps(readyOps, key);
			key->notify();
		}
	}
	return numUpdated;
}

} /* namespace BFX */
